// yolov5_detector.h
#ifndef YOLOV5_DETECTOR_H

#define YOLOV5_DETECTOR_H

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string>
#include <thread>
#include <vector>

#include "global_flags.h"
#include "layer.h"
#include "net.h"

// struct Object {
//   cv::Rect_<float> rect;
//   int label;
//   float prob;
// };

inline float intersection_area(const Object &a, const Object &b) {
  cv::Rect_<float> inter = a.rect & b.rect;
  return inter.area();
}

inline float sigmoid(float x) {
  return static_cast<float>(1.f / (1.f + exp(-x)));
}

class YoloV5Focus : public ncnn::Layer {
public:
  YoloV5Focus() { one_blob_only = true; }

  virtual int forward(const ncnn::Mat &bottom_blob, ncnn::Mat &top_blob,
                      const ncnn::Option &opt) const {
    int w = bottom_blob.w;
    int h = bottom_blob.h;
    int channels = bottom_blob.c;

    int outw = w / 2;
    int outh = h / 2;
    int outc = channels * 4;

    top_blob.create(outw, outh, outc, 4u, 1, opt.blob_allocator);
    if (top_blob.empty())
      return -100;

#pragma omp parallel for num_threads(opt.num_threads)
    for (int p = 0; p < outc; p++) {
      const float *ptr =
          bottom_blob.channel(p % channels).row((p / channels) % 2) +
          ((p / channels) / 2);
      float *outptr = top_blob.channel(p);

      for (int i = 0; i < outh; i++) {
        for (int j = 0; j < outw; j++) {
          *outptr = *ptr;

          outptr += 1;
          ptr += 2;
        }

        ptr += w;
      }
    }

    return 0;
  }
};

void qsort_descent_inplace(std::vector<Object> &faceobjects, int left,
                           int right);
void qsort_descent_inplace(std::vector<Object> &faceobjects);
void nms_sorted_bboxes(const std::vector<Object> &faceobjects,
                       std::vector<int> &picked, float nms_threshold);
void generate_proposals(const ncnn::Mat &anchors, int stride,
                        const ncnn::Mat &in_pad, const ncnn::Mat &feat_blob,
                        float prob_threshold, std::vector<Object> &objects);

ncnn::Net *create_yolov5_net(const std::string &param_path,
                             const std::string &model_path);
int detect_yolov5(const cv::Mat &bgr, std::vector<Object> &objects,
                  const ncnn::Net &yolov5, bool detect_jack);
int detect_yolov5_parallel(const cv::Mat &bgr, std::vector<Object> &objects,
                           const ncnn::Net &yolov5, bool detect_jack);

#endif // YOLOV5_DETECTOR_H
